//
// internal.c
// memory allocation and innards
//
// (c) 2008 why the lucky stiff, the freelance professor
//
#include <stdio.h>
#include <stdlib.h>
#include "potion.h"
#include "internal.h"
#include "gc.h"


// 进程级全局变量， VM 启动时指向其字符串表中的对应保留字字符串（关键字和操作符），
//    用作消息（ Source 类型和 number 类型） 。
// PN $(allocate);

// PNSource 的消息，编译 -> PNProto | nil ；见 potion_source_compile 。
PN $(compile);
PN $(delegated);
PN $(lookup);

// vtable 的消息，设置并返回闭包： self(vtable)[key] = method ；见 potion_def_method 。
// TODO hsq 如何确保编码时 $(def) 等消息只是发给 VT 类型？因为其他类型只是没效果，并不报错。
PN $(def);
// 各实际类型各自实现的消息；搜索 "string" (含双引号)。
PN $(string);
PN $(self), $(call), $(class), $(print);

PN $(if), $(else), $(elsif), $(elseif),
  $(while), $(loop), $(break), $(continue), $(return);

PN $(add), $(sub), $(mult), $(div), $(rem),
  $(bitn), $(bitl), $(bitr), $(pipe), $(caret), $(amp);


// 创建结构 PNData 并返回其指针， .vt=PN_TUSER, .siz=len 。
// PN_MSG(potion_allocate, PN len) {
//   struct PNData *obj = PN_ALLOC_N(PN_TUSER, struct PNData, PN_INT(len));
//   obj->siz = len;
//   return (PN)obj;
// }

static void potion_error_init(Potion *P);
PN_MSG(potion_delegated);

// 构建系统内置虚表对象父子关系树、注册 vtable 的 4 条核心消息；
// 构造 VM 的字符串池，捕获系统消息字符串：关键字和操作符；
// 执行各个子系统初始化：...
static void potion_init(Potion *P) {
  PN vtable, obj_vt;
  // 构建系统内置虚表对象父子关系树
  //  lobby 为根结点， vtable 和 object 为其子节点，其他类型为 object 的子节点
  //  TODO hsq 除了 PN_TFILE PN_TFLEX PN_TSTRINGS PN_TUSER
  // P->lobby = potion_type_new(P, PN_TLOBBY,  0);
  // vtable   = potion_type_new(P, PN_TVTABLE, P->lobby);
  // obj_vt   = potion_type_new(P, PN_TOBJECT, P->lobby);
  // 纯粹为了对齐代码。
  potion_type_new(P, PN_TLOBBY,   0);        P->lobby = PN_VTABLE(PN_TLOBBY);
  potion_type_new(P, PN_TVTABLE,  P->lobby); vtable   = PN_VTABLE(PN_TVTABLE);
  potion_type_new(P, PN_TOBJECT,  P->lobby); obj_vt   = PN_VTABLE(PN_TOBJECT);
  potion_type_new(P, PN_TNIL,     obj_vt);
  potion_type_new(P, PN_TNUMBER,  obj_vt);
  potion_type_new(P, PN_TBOOLEAN, obj_vt);
  potion_type_new(P, PN_TSTRING,  obj_vt);
  potion_type_new(P, PN_TTABLE,   obj_vt);
  potion_type_new(P, PN_TCLOSURE, obj_vt);
  potion_type_new(P, PN_TTUPLE,   obj_vt);
  potion_type_new(P, PN_TSTATE,   obj_vt);
  potion_type_new(P, PN_TSOURCE,  obj_vt);
  potion_type_new(P, PN_TBYTES,   obj_vt);
  potion_type_new(P, PN_TPROTO,   obj_vt);
  potion_type_new(P, PN_TWEAK,    obj_vt);
  potion_type_new(P, PN_TLICK,    obj_vt);
  potion_type_new(P, PN_TERROR,   obj_vt);
  potion_type_new(P, PN_TCONT,    obj_vt);

  potion_str_hash_init(P);
  potion_mod_hash_init(P);

  // 捕获系统消息字符串：关键字
  /* $(allocate) = PN_LITERAL("allocate"); */
  $(break)     = PN_LITERAL("break");
  $(call)      = PN_LITERAL("call");
  $(continue)  = PN_LITERAL("continue");
  $(def)       = PN_LITERAL("def");
  $(delegated) = PN_LITERAL("delegated");
  $(class)     = PN_LITERAL("class");
  $(compile)   = PN_LITERAL("compile");
  $(else)      = PN_LITERAL("else");
  $(elsif)     = PN_LITERAL("elsif");
  $(elseif)    = PN_LITERAL("elseif");
  $(if)        = PN_LITERAL("if");
  $(lookup)    = PN_LITERAL("lookup");
  $(loop)      = PN_LITERAL("loop");
  $(print)     = PN_LITERAL("print");
  $(return)    = PN_LITERAL("return");
  $(self)      = PN_LITERAL("self");
  $(string)    = PN_LITERAL("string");
  $(while)     = PN_LITERAL("while");

  // 捕获系统消息字符串：操作符
  $(add)   = PN_LITERAL("+");
  $(sub)   = PN_LITERAL("-");
  $(mult)  = PN_LITERAL("*");
  $(div)   = PN_LITERAL("/");
  $(rem)   = PN_LITERAL("%");
  $(bitn)  = PN_LITERAL("~");
  $(bitl)  = PN_LITERAL("<<");
  $(bitr)  = PN_LITERAL(">>");
  $(pipe)  = PN_LITERAL("|");
  $(caret) = PN_LITERAL("^");
  $(amp)   = PN_LITERAL("&");

  // 设置 vtable[lookup]=potion_lookup 的闭包
  potion_def_method(P, 0, vtable, $(lookup), PN_FUNC(potion_lookup, "key=o"));
  // 设置 vtable[def]=potion_def_method 的闭包
  // TODO hsq 签名字串 "name=S,block=&" ；及其他。
  potion_def_method(P, 0, vtable, $(def), PN_FUNC(potion_def_method, "name=S,block=&"));

  // 设置 vtable[allocate]=potion_allocate 的闭包
  // potion_send(vtable, $(def), $(allocate), PN_FUNC(potion_allocate, "len=N"));
  // 设置 vtable[delegated]=potion_delegated 的闭包
  potion_send(vtable, $(def), $(delegated), PN_FUNC(potion_delegated, 0));

  potion_vm_init(P);
  potion_rand_init(P);
  potion_lobby_init(P);
  potion_object_init(P);
  potion_error_init(P);
  potion_cont_init(P);
  potion_primitive_init(P);
  potion_num_init(P);
  potion_str_init(P);
  potion_table_init(P);
  potion_source_init(P);
  potion_lick_init(P);
  potion_compiler_init(P);
  potion_file_init(P);
  potion_module_init(P);
  // #ifndef DYLIB
  // potion_lineedit_init(P);
  // #endif

  GC_PROTECT(P);
}

// 分配、管理内存，创建、初始化 VM 。
Potion *potion_create(void *sp) {
  Potion *P = potion_gc_boot(sp);
  P->vt      = PN_TSTATE;
  P->uniq    = (PNUniq)potion_rand_int();
  P->jit_has = POTION_JIT;
  P->jit_on  = POTION_JIT;
  P->in_repl = 0;
  PN_FLEX_NEW(P->vts, PN_TFLEX, PNFlex, TYPE_BATCH_SIZE);
  PN_FLEX_SIZE(P->vts) = PN_TBUILTIN_NUM;
  // 未使用 P->prec = PN_PREC;
  potion_init(P);
  return P;
}

// 销毁 VM ，释放内存。
void potion_destroy(Potion *P) {
  potion_gc_release(P);
}

// 创建空的虚表，附加（注册）到 VM.vts ， .parent=self ；参考 potion_type_new 。
PN_MSG(potion_delegated) {
  PNType t = PN_FLEX_SIZE(P->vts) + PN_TNIL;
  PN_FLEX_NEEDS(1, P->vts, PN_TFLEX, PNFlex, TYPE_BATCH_SIZE);
  return potion_type_new(P, t, self);
}

// 调用闭包 cl.method(P, cl, ...) 并返回其结果；
//  argc 和 argv 决定传入的参数 ... ：argc=0 只传入 cl ，
//    argc=1～15 则传入 argv[0] 开始的 argc 个参数，否则直接返回 nil 。
// TODO hsq 太丑了，简化？
// TODO hsq 如果 argc 与 c->method 不匹配？
// TODO hsq potion_call 通过 potion_vm_proto 调用 potion_vm ，参数数组变成 ... 再变回数组？
PN potion_call(Potion *P, PN cl, PN_SIZE argc, PN * volatile argv) {
  vPN(Closure) c = PN_CLOSURE(cl);
  switch (argc) {
    case 0:
    return c->method(P, cl, cl);
    case 1:
    return c->method(P, cl, argv[0]);
    case 2:
    return c->method(P, cl, argv[0], argv[1]);
    case 3:
    return c->method(P, cl, argv[0], argv[1], argv[2]);
    case 4:
    return c->method(P, cl, argv[0], argv[1], argv[2], argv[3]);
    case 5:
    return c->method(P, cl, argv[0], argv[1], argv[2], argv[3], argv[4]);
    case 6:
    return c->method(P, cl, argv[0], argv[1], argv[2], argv[3], argv[4],
        argv[5]);
    case 7:
    return c->method(P, cl, argv[0], argv[1], argv[2], argv[3], argv[4],
        argv[5], argv[6]);
    case 8:
    return c->method(P, cl, argv[0], argv[1], argv[2], argv[3], argv[4],
        argv[5], argv[6], argv[7]);
    case 9:
    return c->method(P, cl, argv[0], argv[1], argv[2], argv[3], argv[4],
        argv[5], argv[6], argv[7], argv[8]);
    case 10:
    return c->method(P, cl, argv[0], argv[1], argv[2], argv[3], argv[4],
        argv[5], argv[6], argv[7], argv[8], argv[9]);
    case 11:
    return c->method(P, cl, argv[0], argv[1], argv[2], argv[3], argv[4],
        argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]);
    case 12:
    return c->method(P, cl, argv[0], argv[1], argv[2], argv[3], argv[4],
        argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11]);
    case 13:
    return c->method(P, cl, argv[0], argv[1], argv[2], argv[3], argv[4],
        argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11],
        argv[12]);
    case 14:
    return c->method(P, cl, argv[0], argv[1], argv[2], argv[3], argv[4],
        argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11],
        argv[12], argv[13]);
    case 15:
    return c->method(P, cl, argv[0], argv[1], argv[2], argv[3], argv[4],
        argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11],
        argv[12], argv[13], argv[14]);
  }
  return PN_NIL; // TODO: error "too many arguments"
}

// 取 PN obj 类型，即 potion_type(obj) 。
// 未使用 PNType potion_kind_of(PN obj) {
//   return potion_type(obj);
// }

// 构造 Error 对象。
// TODO hsq VM 并未使用 Error ，而是直接打印错误消息；提供 Error 相关消息、测试？
PN potion_error(Potion *P, PN msg, long lineno, long charno, PN excerpt) {
  struct PNError *e = PN_ALLOC(PN_TERROR, struct PNError);
  e->message = msg;
  e->line    = lineno;
  e->chr     = charno;
  e->excerpt = excerpt;
  return (PN)e;
}

// 错误对象 self 的字符串化；
//  如 .excerpt=nil 则只包含 .message ，否则包含全部字段。
PN_MSG(potion_error_string) {
  vPN(Error) e = (struct PNError *)self;
  if (e->excerpt == PN_NIL)
    return potion_str_format(P, "** %s\n", PN_STR_PTR(e->message));
  return potion_str_format(P, "** %s\n"
    "** Where: (line %ld, character %ld) %s\n", PN_STR_PTR(e->message),
    e->line, e->chr, PN_STR_PTR(e->excerpt));
}

// 用任意类型的对象 msg （转为字符串）构造 Error 对象。
// TODO hsq 加入行号等信息？
PN_MSG(potion_error_make, PN msg) {
  if (!PN_IS_STR(msg)) msg = potion_send(msg, $(string));
  return potion_error(P, msg, 0, 0, PN_NIL);
}

// 注册虚表 error 的 string 消息。
static void potion_error_init(Potion *P) {
  PN err_vt = PN_VTABLE(PN_TERROR);
  PN_METHOD(err_vt,   "string", potion_error_string, 0);
  PN_METHOD(P->lobby, "error",  potion_error_make,   "msg=o");
}

// 打印 x 和换行，先发字符串化消息再发打印消息。
// void potion_p(Potion *P, PN x) {
//   STR_AND_PRINT(x);
//   printf("\n");
// }

// 返回当前系统栈地址（粗略）。
void potion_esp(void **esp) {
  PN x;
  *esp = (void *)&x;
}

// dump 寄存器 bp 、系统栈从 VM 开始创建到当前的栈。
// 未使用 void potion_dump_stack(Potion *P) {
//   PN_SIZE n;
//   PN *end, *ebp, *start = P->mem->cstack;
//   POTION_ESP(&end);
//   POTION_EBP(&ebp);
//   #if POTION_STACK_DIR > 0
//     n = end - start;
//   #else
//     n = start - end + 1;
//     start = end;
//     end = P->mem->cstack;
//   #endif

//   printf("-- dumping %u from %p to %p --\n", n, start, end);
//   printf("   ebp = %p, *ebp = %lx\n", ebp, *ebp);
//   while (n--) {
//     printf("   stack(%u) = %lx\n", n, *start);
//     start++;
//   }
// }
